package com.platform.core.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.google.common.base.Strings;
import com.google.common.collect.Maps;
import com.platform.core.repository.SysDepartRepository;
import com.platform.core.repository.SysUserRepository;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.Predicate;
import com.platform.comm.constants.GlobalConstant;
import com.platform.comm.dto.DtoMapper;
import com.platform.comm.enums.YnEnum;
import com.platform.comm.exceptions.RestApiException;
import com.platform.comm.util.StringUtil;
import com.platform.comm.util.TailCall;
import com.platform.comm.util.TailCalls;
import com.platform.comm.web.results.TreeNode;
import com.platform.constants.CommRedisKeyFunc;
import com.platform.core.entity.QNcloudSysUserPostEntity;
import com.platform.core.service.NcloudSysUserPostBiz;
import com.platform.dto.DepartNode;
import com.platform.dto.DepartQo;
import com.platform.dto.DomainTreeNode;
import com.platform.core.entity.QSysDepart;
import com.platform.core.entity.QSysUser;
import com.platform.core.entity.SysDepart;
import com.platform.core.entity.SysUser;
import com.platform.enums.AccessSubOrg;
import com.platform.enums.CommFlagType;
import com.platform.enums.DepartType;
import com.platform.core.security.auth.AuthenticationFacade;
import com.platform.core.service.SysConfTypeService;
import com.platform.core.service.SysDepartService;
import com.platform.utils.UnifiedCodeValid;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.querydsl.QSort;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import javax.annotation.PostConstruct;
import javax.persistence.EntityNotFoundException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

import static com.platform.comm.constants.GlobalConstant.BASE_REDIS_CACHE_PREFIX;

/**
 * Description: 部门信息-Service实现类
 *
 * @author libin Created on 2019/7/18
 **/
@Slf4j
@Service
public class SysDepartServiceImpl extends BaseEntityBizImpl<SysDepartRepository, SysDepart> implements SysDepartService, InitializingBean {

    private final QSysDepart qSysDepart = QSysDepart.sysDepart;
    private final QSysUser qSysUser = QSysUser.sysUser;

    private final DtoMapper dtoMapper;
    private final SysConfTypeService sysConfTypeService;
    private final SysUserRepository sysUserRepository;
    private final RedisTemplate<String, Object> redisTemplate;
    private final NcloudSysUserPostBiz ncloudSysUserPostBiz;
    private final QNcloudSysUserPostEntity userPostEntity = QNcloudSysUserPostEntity.ncloudSysUserPostEntity;

    public SysDepartServiceImpl(SysDepartRepository baseRepository, DtoMapper dtoMapper,
                                SysUserRepository sysUserRepository, RedisTemplate<String, Object> redisTemplate,
                                SysConfTypeService sysConfTypeService, NcloudSysUserPostBiz ncloudSysUserPostBiz) {
        super(baseRepository);
        this.dtoMapper = dtoMapper;
        this.sysUserRepository = sysUserRepository;
        this.redisTemplate = redisTemplate;
        this.sysConfTypeService = sysConfTypeService;
        this.ncloudSysUserPostBiz = ncloudSysUserPostBiz;
    }

    /**
     *  创建组织
     * @param departQo 部门信息
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public DepartQo create(DepartQo departQo) {
        // 创建、修改时间 状态删除标志位
        departQo.init();
        departQo.setInfoSource("local");
        if (StringUtils.isNotEmpty(departQo.getUnifiedCode())){
            if (!UnifiedCodeValid.validate(departQo.getUnifiedCode())){
                throw new RestApiException("统一社会信用代码验证不合法");
            }
        }
        SysDepart entity = dtoMapper.map(departQo, SysDepart.class);
        // 设置route
        if (Strings.isNullOrEmpty(departQo.getParentId())) {
            entity.setLevel(1);
            entity.setRouteCode(departQo.getDepartCode());
            entity.setDomain("0");
            entity.setDepartKind(StringUtils.isNotBlank(departQo.getDepartKind()) ? departQo.getDepartKind() : DepartType.DOMAIN.name());
            entity.setParentId("0");
        } else {
            SysDepart parent = fetchById(departQo.getParentId());
            checkDepartKind(departQo, parent);
            entity.setRouteCode(parent.getRouteCode() + ROUTE_SEPARATOR + departQo.getDepartCode());
            entity.setLevel(parent.getLevel() + 1);
        }
        checkLeadId(departQo);
        checkDepartCode(departQo);
        checkNameUnique(departQo);
        checkUnifiedCode(departQo);
        // 设置管理域
        entity.setDomain(getDomainWhenSave(departQo.getParentId()));
        entity.setId(StringUtil.getUUID());
        entity = saveEntity(entity);
        syncUnifiedCodeRegionId(entity);
        return dtoMapper.map(entity, DepartQo.class);
    }

    /**
     *  验证组织类型是否正确
     * @param departQo
     * @param parent
     */
    private void checkDepartKind(DepartQo departQo, SysDepart parent) {
        // 区域下可以建区域&机构 机构下可以建机构部门 部门下可以建部门&岗位 岗位为叶节点
        DepartType departKind = DepartType.valuesOf(departQo.getDepartKind());
        if(departKind == null) {
            throw new RestApiException("组织类型未知！");
        }
        DepartType parentDepartKind = DepartType.valuesOf(parent.getDepartKind());
        if(parentDepartKind == null) {
            throw new RestApiException("父节点组织类型未知！");
        }
        switch (parentDepartKind) {
            case DOMAIN:
                if(!departKind.equals(DepartType.DOMAIN) && !departKind.equals(DepartType.NORMAL)) {
                    throw new RestApiException("\"区域\"节点下只能是\"区域\"或\"机构\"");
                }
                break;
            case NORMAL:
                if(!departKind.equals(DepartType.DEPART)
                        && !departKind.equals(DepartType.NORMAL)
                        && !departKind.equals(DepartType.POSITION)) {
                    throw new RestApiException("\"机构\"节点下只能是\"机构\"、\"部门\"、\"岗位\"");
                }
                break;
            case DEPART:
                if(!departKind.equals(DepartType.DEPART) && !departKind.equals(DepartType.POSITION)) {
                    throw new RestApiException("\"部门\"节点下只能是\"部门\"或\"岗位\"");
                }
                break;
            case POSITION:
                throw new RestApiException("\"岗位\"节点下不能存在子节点");
            default:
                throw new RestApiException("父节点组织类型未知！");
        }
    }

    /**
     *  组织直接领导是否存在
     * @param departQo
     */
    private void checkLeadId(DepartQo departQo) {
        // 组织直接领导人
        if(StringUtils.isNotBlank(departQo.getLeaderId())
                && sysUserRepository.getOne(departQo.getLeaderId()) == null) {
            throw new RestApiException("所选组织领导用户不存在");
        }
    }

    /**
     *  验证组织编号是否重复
     * @param departQo
     */
    private void checkDepartCode(DepartQo departQo){
        BooleanBuilder builder = buildValidConditions(departQo.getId());
        builder.and(qSysDepart.departCode.eq(Objects.requireNonNull(departQo.getDepartCode()))
                .and(qSysDepart.delFlag.ne(YnEnum.Y)));
        if(StringUtils.isNotBlank(departQo.getId())) {
            builder.and(qSysDepart.id.ne(departQo.getId()));
        }
        if(repository.count(builder) != 0) {
            throw new RestApiException("组织编码重复");
        }
    }

    /**
     *  验证组织名称
     * @param departQo
     */
    public void checkNameUnique(DepartQo departQo) {
        MultiValueMap<String, String> param = new LinkedMultiValueMap<>();
        param.add("parentId",departQo.getParentId());
        param.add("id",StringUtils.isNotEmpty(departQo.getId()) ? departQo.getId() : "");
        param.add("name",departQo.getDepartName());
        param.add("departKind",departQo.getDepartKind());
        param.add("orgType",StringUtils.isNotEmpty(departQo.getOrgType()) ? departQo.getOrgType() : "");
        if(checkNameUnique(param) != 0) {
            throw new RestApiException("相同根节点的组织，名称不能一致");
        }
    }

    /**
     *  社会统一代码重复
     * @param departQo
     */
    public void checkUnifiedCode(DepartQo departQo) {
        if(StringUtils.isNotBlank(departQo.getUnifiedCode()) &&
                checkUnifiedCode(departQo.getId(),departQo.getUnifiedCode()) != 0) {
            throw new RestApiException("社会统一代码重复");
        }
    }


    /**
     *  修改组织信息
     * @param departQo 部门信息
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public DepartQo modify(DepartQo departQo) {
        // 验证社会统一代码
        if (Objects.nonNull(departQo.getUnifiedCode())){
            if (!UnifiedCodeValid.validate(departQo.getUnifiedCode())){
                throw new RestApiException("统一社会信用代码验证不合法");
            }
        }
        // 验证组织信息是否存在
        SysDepart entity = fetchById(departQo.getId());
        if(entity == null) {
            throw new RestApiException("所要修改组织数据不存在");
        }
        // 验证组织领导编号
        checkLeadId(departQo);
        // 验证组织编号是否唯一
        checkDepartCode(departQo);
        // 验证组织名称
        checkNameUnique(departQo);
        // 验证社会统一代码
        checkUnifiedCode(departQo);
        // 验证当前组织类型
        if(StringUtils.isNotBlank(departQo.getParentId())) {
            SysDepart parent = fetchById(departQo.getParentId());
            // 验证 组织类型
            checkDepartKind(departQo,parent);
            departQo.setLevel(parent.getLevel() + 1);
            departQo.setRouteCode(parent.getRouteCode() + ROUTE_SEPARATOR + entity.getDepartCode());
            departQo.setRouteName(parent.getRouteName() + ROUTE_SEPARATOR + entity.getDepartName());
            // 设置管理域(追溯父级区域id)
            departQo.setDomain(getDomainWhenSave(departQo.getParentId()));
        } else {
            departQo.setLevel(1);
            departQo.setRouteCode(departQo.getDepartCode());
            departQo.setDomain("0");
            departQo.setDepartKind(StringUtils.isNotBlank(departQo.getDepartKind()) ? departQo.getDepartKind() : DepartType.DOMAIN.name());
            departQo.setParentId("0");
        }
        // 根据子节点类型验证当前节点类型是否合法
        checkChildDepartKind(departQo,entity);
        // 保存修改
        String oldRouteCode = entity.getRouteCode();
        dtoMapper.map(departQo, entity);
        entity.setUpdateDate(LocalDateTime.now());
        entity = saveEntity(entity);
        // 修改路由编号
        if(StringUtils.equals(oldRouteCode, entity.getRouteCode())) {
            refreshRouteCode(entity);
        }
        syncUnifiedCodeRegionId(entity);
        return dtoMapper.map(entity, DepartQo.class);
    }

    private void refreshRouteCode(SysDepart parent) {
        List<SysDepart> departs = queryAll(qSysDepart.parentId.eq(parent.getId()).and(qSysDepart.delFlag.ne(YnEnum.Y)));
        if(CollectionUtil.isNotEmpty(departs)) {
            departs.forEach(x -> {
                x.setLevel(parent.getLevel() + 1);
                x.setRouteCode(parent.getRouteCode() + ROUTE_SEPARATOR + x.getDepartCode());
                x.setRouteName(parent.getRouteName() + ROUTE_SEPARATOR + x.getDepartName());
                // 设置管理域(追溯父级区域id)
                x.setDomain(getDomainWhenSave(parent));
                saveEntity(x);
                refreshRouteCode(x);
            });
        }
    }

    /**
     * 获取上级机构
     *
     * @param parent 父级部门
     * @return 机构Id
     */
    public String getDomainWhenSave(SysDepart parent) {
        if (parent == null) {
            return "0";
        }
        if (DepartType.DOMAIN.name().equalsIgnoreCase(parent.getDepartKind())) {
            return parent.getId();
        }
        return getDomainWhenSave(fetchById(parent.getParentId()));
    }

    /**
     *  通过子组织的组织类型 验证当前组织类型是否正确
     * @param departQo
     * @param entity
     */
    private void checkChildDepartKind(DepartQo departQo, SysDepart entity) {
        List<SysDepart> departs = queryAll(qSysDepart.parentId.eq(entity.getId())
                .and(qSysDepart.delFlag.ne(YnEnum.Y)));
        if(CollectionUtil.isNotEmpty(departs)) {
            Set<String> kindSet = departs.stream().map(SysDepart::getDepartKind).collect(Collectors.toSet());
            DepartType departKind = DepartType.valuesOf(departQo.getDepartKind());
            if(departKind == null) {
                throw new RestApiException("组织类型未知！");
            }
            switch (departKind) {
                case DOMAIN:
                    if(kindSet.contains(DepartType.POSITION.name()) || kindSet.contains(DepartType.DEPART.name())) {
                        throw new RestApiException("当前节点下有\""+ DepartType.DEPART.getText() +"\"或\""
                                +DepartType.POSITION.getText() +"\"节点，组织类型无法修改为\""
                                + departKind.getText() +"\"类型");
                    }
                    break;
                case NORMAL:
                    if(kindSet.contains(DepartType.DOMAIN.name())) {
                        throw new RestApiException("当前节点下有\""+ DepartType.DOMAIN.getText() +"\"节点，组织类型无法修改为\""
                                + departKind.getText() +"\"类型");
                    }
                    break;
                case DEPART:
                    if(kindSet.contains(DepartType.DOMAIN.name()) || kindSet.contains(DepartType.NORMAL.name() )) {
                        throw new RestApiException("当前节点下有\""+ DepartType.DOMAIN.getText() +"\"或\""
                                +DepartType.NORMAL.getText() +"\"节点，组织类型无法修改为\""
                                + departKind.getText() +"\"类型");
                    }
                    break;
                case POSITION:
                    throw new RestApiException("当前组织节点下存在子组织节点，不能修改为\""+ departKind.getText() +"\"类型");
                default:
                    throw new RestApiException("当前组织类型未知！");
            }
        }
    }

    /**
     *  删除组织信息（只允许无用户绑定，无子组织的组织删除）
     * @param id ID
     */
    @Transactional(rollbackFor = {Exception.class})
    @Override
    public void remove(String id) {
        batchRemove(Collections.singletonList(id));

    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void batchRemove(List<String> ids) {
        ids.forEach(this::preDelete);
        ids.stream().map(this::fetchById)
                .forEach(entity -> {
                    entity.setDelFlag(YnEnum.Y);
                    //        删除岗位时，删除用户岗位信息
                    ncloudSysUserPostBiz.batchRemove(ncloudSysUserPostBiz.queryAll(userPostEntity.departId.eq(entity.getId())));
                    if (entity.getDepartKind().equals(DepartType.NORMAL.name())){
                        redisTemplate.delete(CommRedisKeyFunc.unifiedCodeRegionId(entity.getUnifiedCode()));
                    }
                });
        repository.flush();
    }

    /**
     *  判断存在子组织，存在绑定用户都无法删除
     * @param departId
     */
    private void preDelete(String departId) {
        // 判断是否包含下级区域
        long childCount = repository.count(qSysDepart.parentId.eq(departId).and(qSysDepart.delFlag.eq(YnEnum.N)));
        if(childCount != 0) {
            throw new RestApiException("当前组织包含子组织，无法进行删除操作");
        }
        // TODO：判断有无用户 需要判断组织用户关系表
        long count = sysUserRepository.count(qSysUser.departId.eq(departId).and(qSysUser.delFlag.eq(YnEnum.N)));
        if (count > 0) {
            throw new RestApiException("当前组织绑定了用户，无法进行删除操作");
        }
    }



    @Transactional(rollbackFor = {Exception.class})
    @Override
    public void changeDelFlag(List<String> ids, YnEnum ynEnum) {
        ids.stream().map(this::fetchById)
                .forEach(sysDepart -> sysDepart.setDelFlag(ynEnum));
        repository.flush();
    }


    @Transactional(rollbackFor = {Exception.class})
    @Override
    public void changeStatus(List<String> ids, CommFlagType commFlagType) {
        ids.stream().map(this::fetchById)
                .forEach(sysDepart -> {
                    if (CommFlagType.Disabled.equals(commFlagType) && AuthenticationFacade.getDepartId().equals(sysDepart.getId())) {
                        throw new RestApiException("禁止禁用当前用户组织");
                    }
                    sysDepart.setStatus(commFlagType);
                    sysDepart.setUpdateDate(LocalDateTime.now());
                });
        repository.flush();
    }

    /**
     * 获取上级机构
     *
     * @param parentId 父级ID
     * @return 机构Id
     */
    public String getDomainWhenSave(String parentId) {
        if (Strings.isNullOrEmpty(parentId)) {
            return "0";
        }
        SysDepart parent = fetchById(parentId);
        if (DepartType.DOMAIN.name().equalsIgnoreCase(parent.getDepartKind())) {
            return parent.getId();
        }
        return getDomainWhenSave(parent.getParentId());
    }

    /**
     *  获取组织信息树
     * @param departId 组织id
     * @param departCode 组织编号
     * @param level 0当前 1、2..上级，-1、-2..下级别数据
     * @param ccf 是否包含当前组织
     * @return
     */
    @Override
    public List<DepartQo> searchDepartTree(String departId,String departCode,int level, boolean ccf) {
        List<DepartQo> departQoTree = new ArrayList<>();
        DepartQo baseNode = null;
        if(StringUtils.isNotBlank(departId)) {
            baseNode = findDepartQoById(departId);
        } else if(StringUtils.isNotBlank(departCode)) {
            baseNode = findByDepartCode(departCode);
        } else {
            baseNode = findByDepartCode(getRootCode());
        }
        if(baseNode != null) {
            if(level == 0 && ccf) {
                departQoTree.add(baseNode);
            } else if(level > 0) {
                DepartQo parent = findDepartQoById(baseNode.getParentId());
                if(parent != null && ccf) {
                    parent.getChildren().add(baseNode);
                }
                level--;
                departQoTree.add(findParentNode(parent,level));
            } else {
                List<SysDepart> children = queryAll(qSysDepart.parentId.eq(baseNode.getId()).and(qSysDepart.delFlag.ne(YnEnum.Y)));
                List<DepartQo> departQos = children
                        .stream()
                        .sorted((a,b) -> a.getSortNum().compareTo(b.getSortNum()))
                        .map(c -> dtoMapper.map(c, DepartQo.class))
                        .collect(Collectors.toList());
                if(ccf) {
                    departQoTree.add(baseNode);
                    baseNode.getChildren().addAll(departQos);
                } else {
                    departQoTree.addAll(departQos);
                }
                level ++;
                findChildrenNode(departQos,level);
            }
        }
        return departQoTree;
    }

    private void findChildrenNode(List<DepartQo> departQos,int level) {
        if(CollectionUtil.isNotEmpty(departQos) && level < 0) {
            for(DepartQo x : departQos) {
                int levelNum = level;
                List<SysDepart> children = queryAll(qSysDepart.parentId.eq(x.getId()).and(qSysDepart.delFlag.ne(YnEnum.Y)));
                List<DepartQo> childrenDeparts = children
                        .stream()
                        .sorted((a,b) -> a.getSortNum().compareTo(b.getSortNum()))
                        .map(c -> dtoMapper.map(c, DepartQo.class))
                        .collect(Collectors.toList());
                if(CollectionUtil.isNotEmpty(childrenDeparts)) {
                    x.getChildren().addAll(childrenDeparts);
                }
                findChildrenNode(childrenDeparts,++levelNum);
            }
        }
    }

    private DepartQo findParentNode(DepartQo baseNode, int level) {
        if(level > 0 && baseNode != null) {
            level--;
            DepartQo parent = findDepartQoById(baseNode.getParentId());
            if(parent != null) {
                parent.getChildren().add(baseNode);
                return findParentNode(parent,level);
            } else {
                return baseNode;
            }
        } else {
            return baseNode;
        }
    }

    public DepartQo findDepartQoById(String id) {
        Optional<SysDepart> optional = queryById(id);
        if(!optional.isPresent()) {
            return null;
        }
        return dtoMapper.map(optional.get(), DepartQo.class);
    }


    @Override
    public List<DepartQo> findPositionDepartTree() {
        List<DepartQo> tree = new ArrayList<>();
        Set<String> codeSet = new HashSet<>();
        List<SysDepart> positionDeparts = queryAll(qSysDepart.departKind.eq(DepartType.POSITION.name()).and(qSysDepart.delFlag.ne(YnEnum.Y)));
        positionDeparts.stream().filter(x -> StringUtils.isNotBlank(x.getRouteCode())).forEach(
                depart -> {
                    codeSet.addAll(Arrays.asList(depart.getRouteCode().split(ROUTE_SEPARATOR)));
                }
        );
        if (CollectionUtil.isNotEmpty(codeSet)) {
            List<DepartQo> list = searchDepartTree(null,getRootCode(),Integer.MIN_VALUE, true);
            filterDepartTree(list,tree,codeSet);
        }
        return tree;
    }

    @Override
    public List<DepartQo> findNormalDepart(Map<String, String> param) {
        BooleanBuilder builder = new BooleanBuilder(qSysDepart.departKind.eq(DepartType.NORMAL.name()).and(qSysDepart.delFlag.ne(YnEnum.Y).and(qSysDepart.parentId.ne("0"))));
        String departName = param.get("departName");
        if(StringUtils.isNotBlank(departName)){
            builder.and(qSysDepart.departName.like("%"+departName+"%"));
        }
        List<DepartQo> list = queryAll(builder).stream().map(c -> dtoMapper.map(c, DepartQo.class))
                .collect(Collectors.toList());
        return list;
    }

    private List<DepartQo> filterDepartTree(List<DepartQo> list, List<DepartQo> tree, Set<String> codeSet) {
        if(CollectionUtil.isNotEmpty(list)) {
            for(DepartQo qo : list) {
                if(codeSet.contains(qo.getDepartCode())) {
                    System.out.println(qo.getDepartCode());
                    List<DepartQo> children = qo.getChildren();
                    qo.setChildren(new ArrayList<>());
                    tree.add(qo);
                    qo.getChildren().addAll(filterDepartTree(children,qo.getChildren(),codeSet));
                }
            }
        }
        return new ArrayList<>();
    }

    @Override
    public void refreshAllRouteCode() {
        List<SysDepart> departs = queryAll(qSysDepart.routeCode.eq(getRootCode()).and(qSysDepart.delFlag.ne(YnEnum.Y)));
        departs.forEach(x -> {refreshRouteCode(x);});
    }

    @Override
    public List<SysDepart> findPosition(String departId) {
//        获取部门所属机构信息
        SysDepart depart = searchOrgByDepartId(departId);
//        获取机构下的所有部门id
        List<SysDepart> departs = Lists.newArrayList();
        queryChildrenIdsById(depart.getId()).forEach(id ->
                departs.addAll(queryAll(qSysDepart.departKind.eq(DepartType.POSITION.name())
                        .and(qSysDepart.delFlag.eq(YnEnum.N))
                        .and(qSysDepart.status.eq(CommFlagType.Enabled))
                        .and(qSysDepart.parentId.eq(id)))));
        return departs;
    }

    private SysDepart searchOrgByDepartId(String departId) {
        SysDepart depart = repository.findOne(qSysDepart.id.eq(departId))
                .orElseThrow(() -> new EntityNotFoundException("查询部门不存在"));
        if (!depart.getDepartKind().equals(DepartType.NORMAL.name()) && !depart.getDepartKind().equals(DepartType.DOMAIN.name())){
            return searchOrgByDepartId(depart.getParentId());
        }
        return depart;
    }


    @Override
    public Page<DepartQo> queryDeparts(Predicate predicate, Pageable pageable, MultiValueMap<String, String> parameters, String accessSubOrg) {
        BooleanBuilder builder = buildQueryConditions(predicate, parameters, accessSubOrg);
        if (pageable.getSort().isUnsorted()) {
            pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), QSort.by(qSysDepart.level.asc(),qSysDepart.sortNum.asc(),
                    qSysDepart.createDate.desc()));
        }
        Page<SysDepart> page = queryByPage(builder, pageable);
        return page.map(entity -> {
            DepartQo departQo = dtoMapper.map(entity, DepartQo.class);
            if (departQo.getDepartKind().equals(DepartType.DOMAIN.name())) {
                // 映射转换
                departQo.setRegionName(Objects.toString(getRegionMap().get(departQo.getRegionId()), departQo.getDepartName()));
            }
            return departQo;
        });
    }

    /**
     *  分页查询组织信息
     * @param parameters 查询参数
     * @param pageable   分页参数
     *                   departName 部门名称
     *                   parentId 父级ID
     *                   departKind 部门类型
     *                   orgName 报表使用
     * @return
     */
    @Override
    public Page<DepartQo> queryDeparts(Map<String, String> parameters, Pageable pageable) {
        BooleanBuilder builder = buildConditions(parameters);
        Page<SysDepart> page = queryByPage(builder, pageable);
        Map<String, String> departCodeNameMap = getDepartCodeNameMap();
        return page.map(entity -> {
            DepartQo dto = dtoMapper.map(entity, DepartQo.class);
            dto.setParentName(departCodeNameMap.getOrDefault(dto.getParentId(), "未定义"));
            // 如果有机构名称的检索条件，需要先分组，然后根据判断所属机构是否在传递的机构下
            return dto;
        });
    }

    private BooleanBuilder buildConditions(Map<String, String> params) {
        // 默认查询条件，未删除的，启用的机构
        BooleanBuilder builder = new BooleanBuilder(qSysDepart.delFlag.eq(YnEnum.N)
                .and(qSysDepart.departKind.eq(DepartType.NORMAL.name()))
                .and(qSysDepart.status.eq(CommFlagType.Enabled)));
        final String parentId = params.getOrDefault("parentId", GlobalConstant.DIRECTLY_DEPART);
        final String departName = params.get("departName");
        if (StringUtils.isEmpty(departName)) {
            // 没有输入名称进行检索，默认查询下级机构
            builder.and(qSysDepart.parentId.eq(parentId));
        } else {
            // 如果输入了名称，需要模糊查询出所有。
            builder.and(qSysDepart.departName.like("%"+departName+"%"));
            repository.findById(parentId).ifPresent(dept -> builder.and(qSysDepart.parentId.like(dept.getId() + "%")));
        }

        final String orgName = params.get("orgName");
        if (StringUtils.isNotEmpty(orgName)) {
            // 机构名称，仅适用于报表
            builder.and(qSysDepart.departName.contains(orgName));
        }
        return builder;
    }

    private BooleanBuilder buildQueryConditions(Predicate predicate, MultiValueMap<String, String> parameters, String accessSubOrg) {
        BooleanBuilder builder = new BooleanBuilder();
        final String parentId = parameters.getFirst("parentId");
        final String departName = parameters.getFirst("departName");
        if (AccessSubOrg.Y.getCode().equals(accessSubOrg)) {
            builder = builderConditions(predicate, parameters, SysDepart.class, qSysDepart.getMetadata());
            if (StringUtils.isNotEmpty(parentId)) {
                builder.and(qSysDepart.parentId.eq(parentId).or(qSysDepart.id.eq(parentId)));
            }
            // 添加父节点查询信息
            if (StringUtils.isNotEmpty(departName)) {
                builder.or(qSysDepart.id.eq(Objects.toString(parentId, getRootCode())));
            }
        } else {
            builder.and(qSysDepart.id.eq(Objects.requireNonNull(parentId, "父级id不能为空")));
        }
        final String status = parameters.getFirst("status");
        final String orgKind = parameters.getFirst("departKind");
        if (StringUtils.isNotEmpty(orgKind)) {
            builder.and(qSysDepart.departKind.eq(orgKind));
        }
        if (StringUtils.isEmpty(status) || Objects.equals(status, CommFlagType.Enabled.getFvalue())) {
            builder.and(qSysDepart.status.eq(CommFlagType.Enabled));
        }
        if (StringUtils.isNotEmpty(departName)) {
            builder.and(qSysDepart.departName.contains(departName));
        }
        return builder;
    }

    @SuppressWarnings("unchecked")
    private Map<String, String> getRegionMap() {
        Map<String, String> mappingMap = (Map<String, String>) redisTemplate.opsForValue()
                .get(BASE_REDIS_CACHE_PREFIX + "AllApiCls:region");
        if (mappingMap == null) {
            mappingMap = sysConfTypeService.queryAllApiCls("region");
            redisTemplate.opsForValue().set(BASE_REDIS_CACHE_PREFIX + "AllApiCls:region", mappingMap);
        }
        return mappingMap;
    }

    @SuppressWarnings("unchecked")
    private Map<String, String> getDepartCodeNameMap() {
        Map<String, String> mappingMap = (Map<String, String>) redisTemplate.opsForValue()
                .get(BASE_REDIS_CACHE_PREFIX + "depart:codeMap");
        if (mappingMap == null) {
            mappingMap = queryAllDeparts();
            redisTemplate.opsForValue().set(BASE_REDIS_CACHE_PREFIX + "depart:codeMap", mappingMap);
        }
        return mappingMap;
    }

    @Override
    public long checkUnifiedCode(String id, String unifiedCode) {
        BooleanBuilder builder = buildValidConditions(id);
        if (!(Strings.isNullOrEmpty(unifiedCode) || UNIFIEDCODE_DEFAULT_VALUE.equalsIgnoreCase(unifiedCode))) {
            builder.and(qSysDepart.unifiedCode.eq(unifiedCode));
        }
        return repository.count(builder);
    }

    @Override
    public long checkAreaCode(String id, String bizCode) {
        BooleanBuilder builder = buildValidConditions(id);
        builder.and(qSysDepart.bizCode.eq(bizCode));
        builder.and(qSysDepart.departKind.eq(DepartType.DOMAIN.name()));
        return repository.count(builder);
    }

    @Override
    public long checkDepartName(String parentId, String id, String departName) {
        BooleanBuilder builder = buildValidConditions(id);
        if (!Strings.isNullOrEmpty(parentId)) {
            builder.and(qSysDepart.parentId.eq(parentId));
        }
        builder.and(qSysDepart.departName.eq(departName));
        return repository.count(builder);
    }

    /**
     * 相同根节点的组织，名称不能一致。
     * 针对机构，需要追加机构类型相同名称不能一致。
     */
    @Override
    public long checkNameUnique(MultiValueMap<String, String> params) {
        String parentId = params.getFirst("parentId");
        BooleanBuilder builder = buildValidConditions(params.getFirst("id"));
        if (!Strings.isNullOrEmpty(parentId)) {
            builder.and(qSysDepart.parentId.eq(parentId));
        }
        builder.and(qSysDepart.departName.eq(Objects.requireNonNull(params.getFirst("name"))));
        String departKind = params.getFirst("departKind");
        if (StringUtils.isNotEmpty(departKind) && departKind.equals(DepartType.NORMAL.name())) {
            builder.and(qSysDepart.orgType.eq(Objects.requireNonNull(params.getFirst("orgType"))));
        }
        return repository.count(builder);
    }

    /**
     * 相同根节点的组织，名称不能一致。
     * 针对机构，需要追加机构类型相同名称不能一致。
     */
    @Override
    public long checkCodeUnique(MultiValueMap<String, String> params) {
        String parentId = params.getFirst("parentId");
        BooleanBuilder builder = buildValidConditions(params.getFirst("id"));
        if (!Strings.isNullOrEmpty(parentId)) {
            builder.and(qSysDepart.parentId.eq(parentId));
        }
        builder.and(qSysDepart.departCode.eq(Objects.requireNonNull(params.getFirst("departCode"))));
        return repository.count(builder);
    }

    private BooleanBuilder buildValidConditions(String id) {
        BooleanBuilder builder = new BooleanBuilder(qSysDepart.delFlag.eq(YnEnum.N));
        if (!Strings.isNullOrEmpty(id)) {
            builder.and(qSysDepart.id.ne(id));
        }
        return builder;
    }

    @Override
    public String getDepartById(String departId) {
        //获取当前登陆的用户信息，取到的departCode作为参数传入departTree()中进行查询
        Optional<SysDepart> currentDepart = repository.findOne(qSysDepart.id.eq(AuthenticationFacade.getDepartId()));
        // 如果当前部门为组织结构时 返回其上级区域departCode  如果当前部门为区域时  则直接返回departCode
        SysDepart sysDepart = currentDepart.get();
        if (DepartType.NORMAL.name().equals(sysDepart.getDepartKind())) {
            return sysDepart.getDomain();
        } else {
            return sysDepart.getDepartCode();
        }
    }


    /**
     * 部门树逻辑修改
     * 判断 accessSubOrg  是否可以访问下级组织
     * 是 则返回原逻辑
     * 否 则只查询用户部门信息
     *
     * @param departCode   部门编码
     * @param accessSubOrg 是否允许访问下级组织
     * @return list
     */
    @Cacheable(value = BASE_REDIS_CACHE_PREFIX + "allDept", key = "#departCode.concat('::').concat(#accessSubOrg)", unless = "#result.size() == 0")
    @Override
    public List<DepartNode> departTree(String departCode, String accessSubOrg) {
        BooleanBuilder builder = new BooleanBuilder(qSysDepart.delFlag.eq(YnEnum.N).and(qSysDepart.status.eq(CommFlagType.Enabled)));
        if (AccessSubOrg.Y.getCode().equals(accessSubOrg)) {
            if (Strings.isNullOrEmpty(departCode)) {
                builder.and(qSysDepart.parentId.isNull());
            } else {
                builder.and(qSysDepart.departCode.eq(departCode));
            }
            List<SysDepart> departs = repository.findAll(builder,
                    jpqlQuery -> jpqlQuery.orderBy(qSysDepart.sortNum.asc(), qSysDepart.createDate.desc()));
            return departs.stream().map(this::toDepartNode).collect(Collectors.toList());
        } else {
            List<DepartNode> list = Lists.newArrayList();
            //如果没有部门编码 则返回空
            if (Strings.isNullOrEmpty(departCode)) {
                return list;
            }
            builder.and(qSysDepart.departCode.eq(departCode));
            SysDepart depart = fetchOne(builder);
            list.add(DepartNode.of(depart.getId(), depart.getDepartCode(), depart.getDepartName(),
                    depart.getRegionId(), depart.getDepartKind(), Lists.newArrayList()));
            return list;
        }
    }

    @Override
    public List<DomainTreeNode> domainUserTree(String orgCode, boolean isDomain, Map<String,String> map) {
        BooleanBuilder builder = new BooleanBuilder(qSysDepart.delFlag.eq(YnEnum.N));
        if(Objects.nonNull(map)){
            String departKind = map.get("departKind");
            if(StringUtils.isNotEmpty(departKind)){
                builder.and(QSysDepart.sysDepart.departKind.eq(departKind));
            }
        }
        if(StringUtils.isNotBlank(orgCode)) {
            orgCode = fetchById(orgCode).getRouteCode();
        }
        if (isDomain) {
            // 只显示类型为区域的组织
            if (Strings.isNullOrEmpty(orgCode) || orgCode.equals(ROOT_CODE)) {
                builder.and(qSysDepart.routeCode.like(getRootCode() + "%"));
            } else {
                builder.and(qSysDepart.routeCode.like(orgCode + "%"));
            }
            return toRetList(repository.findAll(builder, jpqlQuery -> jpqlQuery.orderBy(qSysDepart.sortNum.asc(), qSysDepart.createDate.desc()))
                    .stream()
                    .map(dept -> DomainTreeNode.of(dept.getId(), dept.getDepartCode(), dept.getDepartName(), dept.getParentId(), dept.getDepartKind(),
                            dept.getUnifiedCode(),dept.getRegionId(),dept.getOrgType(),dept.getRemark(),dept.getSortNum(),null,dept.getLeaderId(),getLeadName(dept.getLeaderId())))
                    .collect(Collectors.toList()));
        }
        builder.and(qSysDepart.routeCode.like(orgCode + "%"));
        return toRetList(repository.findAll(builder, jpqlQuery -> jpqlQuery.orderBy(qSysDepart.sortNum.asc(), qSysDepart.createDate.desc()))
                .stream()
                .map(dept -> DomainTreeNode.of(dept.getId(), dept.getDepartCode(), dept.getDepartName(), dept.getParentId(), dept.getDepartKind(),
                        dept.getUnifiedCode(), dept.getRegionId(), dept.getOrgType(), dept.getRemark(),dept.getSortNum(), null,dept.getLeaderId(),getLeadName(dept.getLeaderId())))
                .collect(Collectors.toList()));

    }


    private String getLeadName(String leadId) {
        if(StringUtils.isNotBlank(leadId)) {
            Optional<SysUser> userOptional = sysUserRepository.findOne(qSysUser.id.eq(leadId));
            if(userOptional.isPresent()) {
                return userOptional.get().getUsername();
            }
        }
        return null;
    }

    private List<DepartNode> recursive(String parentId) {
        return StreamSupport.stream(repository.findAll(
                qSysDepart.parentId.eq(parentId).and(qSysDepart.delFlag.eq(YnEnum.N).and(qSysDepart.status.eq(CommFlagType.Enabled))),
                QSort.by(qSysDepart.sortNum.asc(), qSysDepart.createDate.desc())).spliterator(), false)
                .map(this::toDepartNode).collect(Collectors.toList());
    }

    private DepartNode toDepartNode(SysDepart depart) {
        return DepartNode.of(depart.getId(), depart.getDepartCode(), depart.getDepartName(),
                depart.getRegionId(), depart.getDepartKind(), recursive(depart.getId()));
    }

    @Override
    public List<String> queryChildrenIdsById(String departId) {
        BooleanBuilder builder = new BooleanBuilder(qSysDepart.delFlag.eq(YnEnum.N)
                .and(qSysDepart.status.eq(CommFlagType.Enabled)));
        if (Strings.isNullOrEmpty(departId)) {
            builder.and(qSysDepart.parentId.isNull());
        } else {
            builder.and(qSysDepart.id.eq(departId));
        }
        List<SysDepart> departs = repository.findAll(builder, jpqlQuery -> jpqlQuery.orderBy(qSysDepart.sortNum.asc()));
        return tailRecursion(new ArrayList<>(),
                departs.stream().map(SysDepart::getId).collect(Collectors.toList()),
                this::queryIds).invoke();
    }

    public List<String> queryIds(List<String> parents) {
        return repository.findAll(
                qSysDepart.parentId.in(parents).and(qSysDepart.delFlag.eq(YnEnum.N)
                        .and(qSysDepart.status.eq(CommFlagType.Enabled))),
                jpqlQuery -> jpqlQuery.orderBy(qSysDepart.departCode.asc())).stream()
                .map(SysDepart::getId).collect(Collectors.toList());
    }

    /**
     * 使用尾递归接口完成
     *
     * @param list    当前递归栈的结果值
     * @param parents 下一个递归需要计算的值
     * @return 尾递归接口, 调用invoke启动及早求值获得结果
     */
    public TailCall<List<String>> tailRecursion(final List<String> list, final List<String> parents,
                                                UnaryOperator<List<String>> handle) {
        if (CollectionUtils.isEmpty(parents)) {
            return TailCalls.done(list);
        } else {
            list.addAll(parents);
            return TailCalls.call(() -> tailRecursion(list, handle.apply(parents), this::queryIds));
        }
    }

    @Override
    public DepartQo findById(String id) {
        SysDepart entity = fetchById(id);
        return dtoMapper.map(entity, DepartQo.class);
    }

    @Override
    public void afterPropertiesSet() {
        log.info("check and set depart code sequence");
    }

    public String genCode() {

        return String.valueOf(redisTemplate.boundValueOps(REDIS_DEPART_CODE_SEQUENCE).increment(10));
    }

    public void setCode(String code){
        redisTemplate.opsForValue().set(REDIS_DEPART_CODE_SEQUENCE, Long.valueOf(code));
    }
    private void checkAndSet() {
        String code = repository.findOne(qSysDepart.departCode.isNotEmpty(), qSysDepart.departCode.max())
                .map(tuple -> tuple.get(qSysDepart.departCode.max()))
                .orElse(ROOT_CODE);
        redisTemplate.opsForValue().set(REDIS_DEPART_CODE_SEQUENCE, Long.valueOf(code));
    }

    @Override
    public DepartQo findByDepartCode(String departCode) {
        return dtoMapper.map(fetchOne(qSysDepart.departCode.eq(departCode)
                .and(qSysDepart.status.eq(CommFlagType.Enabled))
                .and(qSysDepart.delFlag.eq(YnEnum.N))), DepartQo.class);
    }
    @Override
    public DepartQo findByDepartByRouteCode(String routeCode) {
        return dtoMapper.map(fetchOne(qSysDepart.routeCode.eq(routeCode)
                .and(qSysDepart.status.eq(CommFlagType.Enabled))
                .and(qSysDepart.delFlag.eq(YnEnum.N))), DepartQo.class);
    }

    @Override
    public DepartQo findByUnifiedCode(String unifiedCode) {
        SysDepart sysDepart = queryOne(qSysDepart.unifiedCode.eq(unifiedCode)
                .and(qSysDepart.status.eq(CommFlagType.Enabled))
                .and(qSysDepart.delFlag.eq(YnEnum.N))).orElse(null);
        if (Objects.isNull(sysDepart)) {
            return null;
        }
        return dtoMapper.map(sysDepart, DepartQo.class);
    }

    @Override
    public List<String> getDepartIdsWithDomainByCurrent(String departId) {
        DepartQo departQo = findById(departId);
        if (DepartType.DOMAIN.name().equals(departQo.getDepartKind())) {
            return repository.findAll(qSysDepart.status.eq(CommFlagType.Enabled).and(qSysDepart.routeCode.startsWith(departQo.getRouteCode())),qSysDepart.id)
                    .stream().map(tuple -> tuple.get(qSysDepart.id)).collect(Collectors.toList());
        } else {
            return Collections.singletonList(departId);
        }
    }

    @Override
    public List<DepartQo> getDepartWithDomain(String departId) {
        DepartQo departQo = findById(departId);
        if (DepartType.DOMAIN.name().equals(departQo.getDepartKind())) {
            return queryAll(qSysDepart.status.eq(CommFlagType.Enabled)
                    .and(qSysDepart.departCode.startsWith(prefixCode(departQo.getDepartCode(), departQo.getLevel())))).stream()
                    .distinct()
                    .map(depart -> dtoMapper.map(depart, DepartQo.class))
                    .collect(Collectors.toList());
        } else {
            return Collections.singletonList(departQo);
        }
    }

    @Override
    public List<String> getDeptIdsWithPid(String departId,String childLv) {
        if ("2".equals(childLv)){
            return queryAll(qSysDepart.status.eq(CommFlagType.Enabled)
                    .and(qSysDepart.parentId.in(queryAll(qSysDepart.status.eq(CommFlagType.Enabled)
                            .and(qSysDepart.parentId.eq(departId))).stream()
                            .map(SysDepart::getId)
                            .collect(Collectors.toList())))).stream()
                    .map(SysDepart::getId)
                    .collect(Collectors.toList());
        }else{
            return queryAll(qSysDepart.status.eq(CommFlagType.Enabled)
                    .and(qSysDepart.parentId.eq(departId))).stream()
                    .map(SysDepart::getId)
                    .collect(Collectors.toList());
        }
    }


    @Override
    public List<String> getDepartIdsWithDomainById(String departId) {
        DepartQo departQo = findById(departId);
        if (DepartType.DOMAIN.name().equals(departQo.getDepartKind())) {
            return queryAll(qSysDepart.status.eq(CommFlagType.Enabled)
                    .and(qSysDepart.departKind.eq("NORMAL"))
                    .and(qSysDepart.departCode.startsWith(prefixCode(departQo.getDepartCode(), departQo.getLevel())))).stream()
                    .distinct()
                    .map(SysDepart::getId)
                    .collect(Collectors.toList());
        } else {
            return Collections.singletonList(departId);
        }
    }

    @Override
    public TreeNode departDomainTree() {
        Optional<SysDepart> optional = repository.findOne(new BooleanBuilder(qSysDepart.departCode.eq(ROOT_CODE)));
        return domainTreeNode(optional.orElseThrow(() -> new RestApiException("根管理域不存在")));
    }

    @Override
    public List<String> queryDepartIdByName(String departName) {
        BooleanBuilder builder = new BooleanBuilder(qSysDepart.delFlag.eq(YnEnum.N)
                .and(qSysDepart.departName.eq(departName))
                .and(qSysDepart.status.eq(CommFlagType.Enabled)));

        return StreamSupport
                .stream(repository.findAll(builder).spliterator(), false)
                .map(SysDepart::getId)
                .collect(Collectors.toList());
    }

    private List<TreeNode> querySubDomain(String parentId) {
        return StreamSupport.stream(repository.findAll(
                qSysDepart.domain.eq(parentId).and(qSysDepart.delFlag.eq(YnEnum.N)
                        .and(qSysDepart.status.eq(CommFlagType.Enabled))), QSort.by(qSysDepart.sortNum.asc(),
                        qSysDepart.createDate.desc())).spliterator(), false)
                .map(this::domainTreeNode).collect(Collectors.toList());
    }

    private TreeNode domainTreeNode(SysDepart depart) {
        return TreeNode.of(depart.getId(), depart.getDepartCode(),
                depart.getDepartName(), querySubDomain(depart.getId()));
    }

    @Override
    public Map<String, String> queryAllDeparts() {
        Map<String, String> retMap = Maps.newHashMap();
        BooleanBuilder builder = new BooleanBuilder();
        builder.and(qSysDepart.status.eq(CommFlagType.Enabled));
        builder.and(qSysDepart.delFlag.eq(YnEnum.N));
        repository.findAll(builder, qSysDepart).forEach(tuple -> {
            SysDepart sysDepart = tuple.get(qSysDepart);
            retMap.put(Objects.requireNonNull(sysDepart).getId(), sysDepart.getDepartName());
        });
        return retMap;
    }

    @Override
    public String getRootCode() {
        return ROOT_CODE;
    }

    /**
     * 根据 区域代码 查询子集区域
     * <p>
     * 第一、二位表示省（自治区、直辖市、特别行政区）。
     * <p>
     * 第三、四位表示市（地区、自治州、盟及国家直辖市所属市辖区和县的汇总码）。其中，01-20，51-70表示省直辖市；21-50表示地区（自治州、盟）。
     * <p>
     * 第五、六位表示县（市辖区、县级市、旗）。01-20表示市辖区或地区（自治州、盟）辖县级市；21-70表示县（旗）；81-99表示省直辖县级市；
     * 71-80表示工业园区或者经济开发区。
     * <p>
     * 第七至九位表示乡、镇（街道办事处）。其中000-099表示街道办事处，100-199表示镇，200-299表示乡，400-479表示林场、牧场、科技城、园区，
     * 480-499表示林业管理局，500-579表示农场、团，580-599表示畜牧场。
     * <p>
     * 为了更详细地反映乡镇以下区划情况，国家统计局补充了三位表示居委会、村委会的代码。
     *
     * @param regCode 区域编码
     * @return 编码前缀
     */
    public String prefixCode(String regCode) {
        // 根据区域编码查询查询 组织
        DepartQo departQo = findByDepartCode(regCode);
        if (DepartType.NORMAL.name().equals(departQo.getDepartKind())) {
            return regCode;
        }
        return this.prefixCode(departQo.getDepartCode(), departQo.getLevel());
    }


    /**
     * 根据区域编码和 级别 返回 区域代码前缀
     *
     * @param departCode 区域代码
     * @param level      级别
     * @return 编码前缀
     */
    public String prefixCode(String departCode, Integer level) {
        // 根据区域编码查询查询 组织
        if (Objects.isNull(level) || level == 0) {
            log.error("区域类型的组织,组织级别不能为null");
            return "NULL";
        }

        if (level <= 4) {
            return departCode.substring(0, 2 * (level - 1));
        }

        if (level == 5) {
            return departCode.substring(0, 9);
        }

        if (level == 6) {
            return departCode;
        }
        return "NULL";
    }

    @Override
    public SysDepart getDepartByUserId(String userId) {
        return repository.findOne(qSysUser.id.eq(userId), sql -> sql.from(qSysDepart).leftJoin(qSysUser)
                .on(qSysUser.departId.eq(qSysDepart.id)), qSysDepart)
                .map(tuple -> tuple.get(qSysDepart)).orElse(null);
    }
    @Override
    public DepartQo queryDepartByUserId(String userId) {
        SysDepart sysDepart = repository.findOne(qSysUser.id.eq(userId), sql -> sql.from(qSysDepart).leftJoin(qSysUser)
                .on(qSysUser.departId.eq(qSysDepart.id)), qSysDepart)
                .map(tuple -> tuple.get(qSysDepart)).orElse(null);
        return dtoMapper.map(sysDepart, DepartQo.class);
    }

    @Override
    public DepartQo findByDeptUnifiedCode(String deptUnifiedCode) {
        BooleanBuilder builder = new BooleanBuilder();
        builder.and(qSysDepart.status.eq(CommFlagType.Enabled))
                .and(qSysDepart.delFlag.eq(YnEnum.N))
                .and(qSysDepart.unifiedCode.eq(deptUnifiedCode));
        long count = repository.count(builder);
        Assert.isTrue(count == 1, "系统内部错误");
        SysDepart sysDepart = repository.findOne(builder).orElse(null);
        return dtoMapper.map(sysDepart, DepartQo.class);
    }

    @Override
    public List<DomainTreeNode> leftDomainTree() {
        BooleanBuilder builder = new BooleanBuilder(qSysDepart.delFlag.eq(YnEnum.N).and(qSysDepart.status.eq(CommFlagType.Enabled)));
        // 只显示类型为区域的组织
        builder.and(qSysDepart.departKind.eq(DepartType.DOMAIN.name()));
        builder.and(qSysDepart.departCode.like(prefixCode(getRootCode()) + "%"));
        List<DomainTreeNode> departs = repository.findAll(builder,
                jpqlQuery -> jpqlQuery.orderBy(qSysDepart.sortNum.asc(), qSysDepart.createDate.desc()))
                .stream().map(dept -> DomainTreeNode.of(dept.getId(), dept.getDepartCode(),
                        dept.getDepartName(), dept.getParentId(), null)).collect(Collectors.toList());
        return toRetList(departs);
    }

    private List<DomainTreeNode> toRetList(List<DomainTreeNode> departs) {
        //转换成树结构
        Map<String, List<DomainTreeNode>> collect = departs.stream()
                .collect(Collectors.groupingBy(DomainTreeNode::getParentId));
        Map<String, List<DomainTreeNode>> maps = Maps.newHashMap();
        collect.forEach((key, children) -> children.forEach(depart -> {
            if (collect.containsKey(depart.getId())) {
                depart.setChildren(collect.get(depart.getId()));
                maps.put(depart.getId(), collect.get(depart.getId()));
            }
        }));
        List<DomainTreeNode> result = Lists.newArrayList();
        //过滤重复的子级部门数据
        collect.entrySet().stream().filter(e -> !maps.containsKey(e.getKey())).forEach(e -> result.addAll(e.getValue()));
        return result;
    }

    @Override
    public Page<DepartQo> searchPage(Predicate predicate, Pageable pageable, MultiValueMap<String, String> parameters) {
        if (pageable.getSort().isUnsorted()) {
            pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), QSort.by(qSysDepart.sortNum.asc(),
                    qSysDepart.createDate.desc()));
        }
        BooleanBuilder builder = builderConditions(predicate, parameters, SysDepart.class, qSysDepart.getMetadata());
        final String status = parameters.getFirst("status");
        final String departName = parameters.getFirst("departName");
        final String parentId = parameters.getFirst("parentId");
        final String departKind = parameters.getFirst("departKind");
        if (Strings.isNullOrEmpty(status) || Objects.equals(status, CommFlagType.Enabled.getFvalue())) {
            builder.and(qSysDepart.status.eq(CommFlagType.Enabled));
        }
        if (StringUtils.isNotBlank(departName)) {
            builder.and(qSysDepart.departName.contains(departName));
        }
        if (StringUtils.isNotBlank(parentId)) {
            builder.and(qSysDepart.parentId.eq(parentId));
        }
        if (StringUtils.isNotBlank(departKind)) {
            builder.and(qSysDepart.departKind.eq(departKind));
        }

        return queryByPage(builder, pageable).map(entity -> {
            DepartQo departQo = dtoMapper.map(entity, DepartQo.class);
            if (departQo.getDepartKind().equals(DepartType.DOMAIN.name())) {
                // 映射转换
                departQo.setRegionName(Objects.toString(getRegionMap().get(departQo.getRegionId()), departQo.getDepartName()));
            }
            return departQo;
        });
    }

    @Override
    public void syncSubsystem(String id) {
        // todo
    }

    @Override
    public String getDomainDepartCode(String departId) {
        DepartQo departQo = findById(departId);
        return departQo.getId();
    }

    @Override
    public List<DepartNode> children(String parentId, String status, String departKind) {
        BooleanBuilder builder = new BooleanBuilder(qSysDepart.delFlag.eq(YnEnum.N));
        if (StringUtils.isNotBlank(status)) {
            builder.and(qSysDepart.status.eq(CommFlagType.getFlagByValue(status)));
        }
        builder.and(qSysDepart.parentId.eq(parentId));
        if (StringUtils.isNotBlank(departKind)) {
            builder.and(qSysDepart.departKind.eq(departKind));
        }
        List<SysDepart> departs = repository.findAll(builder, jpqlQuery -> jpqlQuery.orderBy(qSysDepart.sortNum.asc()));
        return departs.stream().map(this::transToDepartNode).collect(Collectors.toList());
    }


    @Override
    public List<DepartNode> rootDepart(String departCode, String status, String departKind) {
        Optional<SysDepart> optional = queryOne(qSysDepart.departCode.eq(departCode).and(qSysDepart.delFlag.eq(YnEnum.N)));
        SysDepart root = optional.get();
        List<DepartNode> childrens = this.children(root.getId(), status, departKind);
        DepartNode rootNode = transToDepartNode(root);
        rootNode.setChildren(childrens);
        List<DepartNode> departNodes = Lists.newArrayList();
        departNodes.add(rootNode);
        return departNodes;
    }

    @Override
    public List<DepartQo> queryDepartByKind(String parentId, String departKind, String addSelf) {
        if (StringUtils.isNotEmpty(departKind) && "ALL".equals(departKind)) {
            String domainDepartId = GlobalConstant.HE_BEI_DEAPRT;
            String normalDepartId = GlobalConstant.DIRECTLY_DEPART;
            if (StringUtils.isNotEmpty(parentId)) {
                domainDepartId = parentId;
                normalDepartId = parentId;
            }
            List<DepartQo> domainDeparts = getDepartByKind(domainDepartId, DepartType.DOMAIN.name(), YnEnum.N.name());
            List<DepartQo> normalDeparts = getDepartByKind(normalDepartId, DepartType.NORMAL.name(), YnEnum.N.name());
            domainDeparts.addAll(normalDeparts);
            return domainDeparts;
        } else {
            return getDepartByKind(parentId, departKind, addSelf);
        }
    }

    @Override
    public Page<DepartQo> getSubDepartPage(String region, String normal, int level, Pageable pageInfo) {
        BooleanBuilder builder = new BooleanBuilder(qSysDepart.delFlag.eq(YnEnum.N));
        builder.and(qSysDepart.status.eq(CommFlagType.Enabled))
                .and(qSysDepart.parentId.eq(region))
                .and(qSysDepart.level.eq(level))
                .and(qSysDepart.departKind.eq(normal));
        Page<SysDepart> departs = repository.findAll(builder, pageInfo, jpqlQuery -> jpqlQuery.orderBy(qSysDepart.sortNum.asc()));
        return departs.map(sysDepart -> dtoMapper.map(sysDepart, DepartQo.class));
    }

    @Override
    public Page<DepartQo> queryPageByRegion(String region, String normal, Pageable pageInfo) {
        BooleanBuilder builder = new BooleanBuilder(qSysDepart.delFlag.eq(YnEnum.N));
        builder.and(qSysDepart.status.eq(CommFlagType.Enabled))
                .and(qSysDepart.parentId.eq(region))
                .and(qSysDepart.departKind.eq(normal));
        Page<SysDepart> departs = repository.findAll(builder, pageInfo, jpqlQuery -> jpqlQuery.orderBy(qSysDepart.sortNum.asc()));
        return departs.map(sysDepart -> dtoMapper.map(sysDepart, DepartQo.class));
    }

    private DepartNode transToDepartNode(SysDepart depart) {
        return DepartNode.of(depart.getId(), depart.getDepartCode(), depart.getDepartName(),
                depart.getRegionId(), depart.getDepartKind(), Lists.newArrayList());
    }

    private List<DepartQo> getDepartByKind(String parentId, String departKind, String addSelf) {
        BooleanBuilder builder = new BooleanBuilder(qSysDepart.delFlag.eq(YnEnum.N));
        builder.and(qSysDepart.status.eq(CommFlagType.Enabled));
        if (StringUtils.isNotBlank(parentId)) {
            builder.and(qSysDepart.parentId.eq(parentId));
        }
        if (StringUtils.isNotBlank(departKind)) {
            builder.and(qSysDepart.departKind.eq(departKind));
            // 查询区域时过滤省政府区域
            if ("DOMAIN".equals(departKind)) {
                builder.and(qSysDepart.id.ne(GlobalConstant.DIRECTLY_DEPART));
            }
        }
        if ("Y".equals(addSelf)) {
            builder.or(qSysDepart.id.eq(parentId));
        }
        List<SysDepart> departs = repository.findAll(builder, jpqlQuery -> jpqlQuery.orderBy(qSysDepart.sortNum.asc()));
        return departs.stream().map(sysDepart -> dtoMapper.map(sysDepart, DepartQo.class)).collect(Collectors.toList());
    }

    @Override
    public void unifiedCodeRegionId() {
        initUnifiedCodeRegionId(false);
    }

    @PostConstruct //项目启动时执行一次
    public void initUnifiedCodeRegionId() {
        initUnifiedCodeRegionId(true);
    }

    private void initUnifiedCodeRegionId(Boolean isInit){
        log.info("initUnifiedCodeRegionId start");
        long start = System.currentTimeMillis();
        final Set<String> keys = redisTemplate.keys(CommRedisKeyFunc.unifiedCodeRegionId("*"));
        if (isInit && CollUtil.isNotEmpty(keys)){
            //初始化，当发现key不为空时，不执行
            log.info("initUnifiedCodeRegionId end,because redis has "
                    +keys.size()+" keys");
            return;
        }
        List<SysDepart> departs = queryAll(qSysDepart.delFlag.eq(YnEnum.N)
                .and(qSysDepart.departKind.eq(DepartType.NORMAL.name()))
                .and(qSysDepart.status.eq(CommFlagType.Enabled)));
        redisTemplate.delete(keys);
        redisTemplate.execute(connection -> {
            for (final SysDepart sysDepart: departs) {
                if (StrUtil.isNotBlank(sysDepart.getUnifiedCode())){
                    final byte[] key = redisTemplate.getStringSerializer().serialize(CommRedisKeyFunc.unifiedCodeRegionId(sysDepart.getUnifiedCode()));
                    // 保存区划代码同时也保存对应得部门ID,中间用:隔开
                    final byte[] value = redisTemplate.getStringSerializer().serialize(sysDepart.getRegionId()+":"+sysDepart.getRouteCode());
                    connection.set(key, value);
                }
            }
            return true;
        }, false, true);
        log.info("initUnifiedCodeRegionId success,take up time is "
                +(System.currentTimeMillis()-start)+" ms");
    }

    /**
     * 同步 统一码 - 区划:机构id
     * @param sysDepart
     */
    private void syncUnifiedCodeRegionId(SysDepart sysDepart){
        if (sysDepart.getDepartKind().equals(DepartType.NORMAL.name())){
            redisTemplate.execute(connection -> {
                if (StrUtil.isNotBlank(sysDepart.getUnifiedCode())){
                    final byte[] key = redisTemplate.getStringSerializer().serialize(CommRedisKeyFunc.unifiedCodeRegionId(sysDepart.getUnifiedCode()));
                    // 保存区划代码同时也保存对应得部门ID,中间用:隔开
                    final byte[] value = redisTemplate.getStringSerializer().serialize(sysDepart.getRegionId()+":"+sysDepart.getRouteCode());
                    connection.set(key, value);
                }
                return true;
            }, false, true);
        }
    }

    @Override
    public DepartQo queryDepartById(String departId) {
        //获取当前登陆的用户信息，取到的departCode作为参数传入departTree()中进行查询
        Optional<SysDepart> currentDepart = repository.findOne(qSysDepart.id.eq(departId));
        SysDepart sysDepart = currentDepart.get();
        DepartQo departNode = dtoMapper.map(sysDepart, DepartQo.class);
        return departNode;
    }


    @Override
    public List<String> getChildDepartIdsById(String departId) {
        SysDepart sysDepart = queryById(departId).orElseThrow(() -> new RestApiException("该部门不存在！"));
        return repository.findAll(qSysDepart.delFlag.eq(YnEnum.N)
                .and(qSysDepart.routeCode.contains(sysDepart.getDepartCode()))
                .and(qSysDepart.status.eq(CommFlagType.Enabled)), qSysDepart.id)
                .stream().map(tuple -> tuple.get(qSysDepart.id)).collect(Collectors.toList());
    }

    @Override
    public List<String> getDepartId(Map<String, String> map) {
        String departName = map.getOrDefault("departName", "");
        String routeCode = map.getOrDefault("routeCode", "");
        BooleanBuilder builder = new BooleanBuilder();
        builder.and(qSysDepart.status.eq(CommFlagType.Enabled));
        if (StringUtils.isNotBlank(departName)) {
            builder.and(qSysDepart.departName.contains(departName));
        }
        if (StringUtils.isNotBlank(routeCode)) {
            builder.and(qSysDepart.routeCode.contains(routeCode));
        }
        return queryAll(builder).stream().map(SysDepart::getId).collect(Collectors.toList());
    }
}
